package com.gxp.framework.aspectj;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import com.gxp.common.annotation.Audit;
import com.gxp.common.config.GxPConfig;
import com.gxp.common.constant.Constants;
import com.gxp.common.core.domain.entity.SysUser;
import com.gxp.common.utils.DateUtils;
import com.gxp.common.utils.ServletUtils;
import com.gxp.common.utils.ShiroUtils;
import com.gxp.common.utils.StringUtils;
import com.gxp.framework.manager.AsyncManager;
import com.gxp.framework.manager.factory.AsyncFactory;
import com.gxp.system.domain.ZkAudit;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;

import java.util.Map;

@Aspect
@Component
public class AuditAspect {

    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    /** 排除敏感属性字段 */
    public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };

    /** 计算操作消耗时间 */
    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");

    /**
     * 处理请求前执行
     */
    @Before(value = "@annotation(controllerLog)")
    public void boBefore(JoinPoint joinPoint, Audit controllerLog)
    {
        TIME_THREADLOCAL.set(System.currentTimeMillis());
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Audit controllerLog, Object jsonResult)
    {
        handleLog(joinPoint, controllerLog, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e 异常
     */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Audit controllerLog, Exception e)
    {
        handleLog(joinPoint, controllerLog, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, Audit controllerLog, final Exception e, Object jsonResult)
    {
        try
        {
            // 获取当前的用户
            SysUser currentUser = ShiroUtils.getSysUser();

            // *========数据库日志=========*//
            ZkAudit audit = new ZkAudit();
            audit.setOperStatus(Constants.SUCCESS);
            // 请求的地址
            String ip = ShiroUtils.getIp();
            audit.setIp(ip);
            if (currentUser != null){
                audit.setWho(ShiroUtils.getLoginName());
                audit.setRole(ShiroUtils.getRoleNames());
            }
            if(e != null){
                audit.setOperStatus(Constants.FAIL);
                audit.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            String jsonString = JSONObject.toJSONString(jsonResult);
            JSONObject jsonObject = null;

            if (StringUtils.isNotEmpty(jsonString)) {

                jsonObject = JSONObject.parseObject(jsonString);
                // 带上实体Id
                String data = jsonObject.getString("entityId");
                if (StringUtils.isNotEmpty(data)) {
                    audit.setEntityId(Long.valueOf(data));
                }
            }

            audit.setWhat(controllerLog.description());
            audit.setTitle(controllerLog.businessTitle().getInfo());
            audit.setType(controllerLog.businessAction().ordinal());
            Map<String, String[]> map = ServletUtils.getRequest().getParameterMap();
            if(jsonObject != null){
                //移除固定属性
                jsonObject.remove("msg");
                jsonObject.remove("code");
                jsonObject.remove("data");
                jsonObject.remove("entityId");
                //剩下属性转为String存储
                String params = JSONObject.toJSONString(jsonObject, excludePropertyPreFilter(controllerLog.excludeParamNames()));
//                String params = JSONObject.toJSONString(map, excludePropertyPreFilter(controllerLog.excludeParamNames()));
                audit.setAuditParams(StringUtils.substring(params, 0, 2000));
            }

            UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
            // 获取客户端浏览器
            String browser = userAgent.getBrowser().getName();
            audit.setWher(browser);
            audit.setTime(DateUtils.getNowDate());
            audit.setVersion(GxPConfig.getVersion());
            audit.setComment("");
            AsyncManager.me().execute(AsyncFactory.recordAudit(audit));
        }
        catch (Exception exp)
        {
            // 记录本地异常日志
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
        finally
        {
            TIME_THREADLOCAL.remove();
        }
    }

    /**
     * 忽略敏感属性
     */
    public PropertyPreFilters.MySimplePropertyPreFilter excludePropertyPreFilter(String[] excludeParamNames)
    {
        return new PropertyPreFilters().addFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
    }
}
